﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Text.RegularExpressions;

namespace PdfLib
{
    /// <summary>
    /// infer grid (lines and rects) from pdf paths
    /// </summary>
    public class PdfDrawingInstructionReader
    {
        /// <summary>
        /// if current page have any pdf path
        /// </summary>
        /// <param name="pdfFilePath"></param>
        /// <param name="pageNum"></param>
        /// <returns></returns>
        public static bool HaveDrawingInstructionsInPage(string pdfFilePath, int pageNum)
        {
            bool hasDrawingInstruction = true;
            PDFPARSERLib.Document doc = new PDFPARSERLib.Document();
            PDFPARSERLib.Content content = new PDFPARSERLib.Content();
            if (doc.Open(pdfFilePath, ""))
            {
                doc.PageNo = pageNum;
                PDFPARSERLib.Page page = doc.Page;
                content = page.Content;
                if (string.IsNullOrEmpty(content.GetNextPath()))
                {
                    hasDrawingInstruction = false;
                }
                doc.Close();
            }
            return hasDrawingInstruction;
        }

        /// <summary>
        /// retrieve all drawing instruction from current page
        /// only determine LineTo and Rect instructions
        /// adjust from position using Move instruction
        /// </summary>
        /// <param name="pdfFilePath"></param>
        /// <param name="pageNum"></param>
        /// <returns></returns>
        public static List<PdfDrawingInstruction> GetDrawingInstructionsInPage(string pdfFilePath, int pageNum)
        {
            SizeF pageSize = PdfPropertyReader.GetPageSize(pdfFilePath, pageNum);
            List<PdfDrawingInstruction> instructions = new List<PdfDrawingInstruction>();
            PDFPARSERLib.Document doc = new PDFPARSERLib.Document();
            PDFPARSERLib.Content content = new PDFPARSERLib.Content();
            if (doc.Open(pdfFilePath, ""))
            {
                doc.PageNo = pageNum;
                PDFPARSERLib.Page page = doc.Page;
                content = page.Content;
                PointF lastPos = PointF.Empty;
                PointF currentPos = PointF.Empty;
                Regex regex;
                Match match;
                string instructionStr = content.GetNextPath();
                while (!string.IsNullOrEmpty(instructionStr))
                {
                    string[] pathNodes = instructionStr.Trim().Split(new char[] { '\n' });
                    for (int i = 0; i < pathNodes.Length; i++)
                    {
                        string path = pathNodes[i];
                        if (path.EndsWith(" m"))
                        {
                            regex = new Regex("^(-?\\d+\\.?\\d*)\\s+(-?\\d+\\.?\\d*)\\s+m");
                            match = regex.Match(path);
                            if (match.Success)
                            {
                                float x = float.Parse(match.Groups[1].Value);
                                float y = float.Parse(match.Groups[2].Value);
                                y = pageSize.Height - y;
                                lastPos = new PointF(x, y);
                            }
                        }
                        else if (path.EndsWith(" l"))
                        {
                            regex = new Regex("^(-?\\d+\\.?\\d*)\\s+(-?\\d+\\.?\\d*)\\s+l");
                            match = regex.Match(path);
                            if (match.Success)
                            {
                                float x = float.Parse(match.Groups[1].Value);
                                float y = float.Parse(match.Groups[2].Value);
                                y = pageSize.Height - y;
                                if (lastPos == PointF.Empty)
                                    lastPos = new PointF(x, y);
                                else
                                {
                                    currentPos = new PointF(x, y);
                                    PdfDrawingInstruction pdfDraw = new PdfDrawingInstruction();
                                    pdfDraw.DrawingType = PdfDrawType.DrawLine;
                                    pdfDraw.FromPos = lastPos;
                                    pdfDraw.ToPos = currentPos;
                                    pdfDraw.Instruction = instructionStr;
                                    instructions.Add(pdfDraw);
                                    lastPos = currentPos;
                                }
                            }
                        }
                        else if (path.EndsWith(" re"))
                        {
                            regex = new Regex("^(-?\\d+\\.?\\d*)\\s+(-?\\d+\\.?\\d*)\\s+(-?\\d+\\.?\\d*)\\s+(-?\\d+\\.?\\d*)\\s+re");
                            match = regex.Match(path);
                            if (match.Success)
                            {
                                float x = float.Parse(match.Groups[1].Value);
                                float y = float.Parse(match.Groups[2].Value);
                                float w = float.Parse(match.Groups[3].Value);
                                float h = float.Parse(match.Groups[4].Value);
                                y = pageSize.Height - y;
                                if (h < 0)
                                    h = 0 - h;
                                PdfDrawingInstruction pdfDraw = new PdfDrawingInstruction();
                                pdfDraw.DrawingType = PdfDrawType.DrawRect;
                                pdfDraw.FromPos = new PointF(x, y);
                                pdfDraw.ToPos = new PointF(x + w, y + h);
                                pdfDraw.Instruction = instructionStr;
                                instructions.Add(pdfDraw);
                            }
                        }
                        else if (path == "f")
                        {
                            if (i > 0)
                            {
                                string lastPath = pathNodes[i - 1];
                                if (lastPath.EndsWith(" l"))
                                {
                                    regex = new Regex("^(-?\\d+\\.?\\d*)\\s+(-?\\d+\\.?\\d*)\\s+l");
                                    match = regex.Match(lastPath);
                                    if (match.Success)
                                    {
                                        float x = float.Parse(match.Groups[1].Value);
                                        float y = float.Parse(match.Groups[2].Value);
                                        y = pageSize.Height - y;
                                        // lastPos = new System.Drawing.PointF(x, y);
                                    }
                                }
                                else if (lastPath.EndsWith(" re"))
                                {
                                    regex = new Regex("^(-?\\d+\\.?\\d*)\\s+(-?\\d+\\.?\\d*)\\s+(-?\\d+\\.?\\d*)\\s+(-?\\d+\\.?\\d*)\\s+re");
                                    match = regex.Match(lastPath);
                                    if (match.Success)
                                    {
                                        float x = float.Parse(match.Groups[1].Value);
                                        float y = float.Parse(match.Groups[2].Value);
                                        float w = float.Parse(match.Groups[3].Value);
                                        float h = float.Parse(match.Groups[4].Value);
                                        y = pageSize.Height - y;
                                        if (h < 0)
                                            h = 0 - h;
                                        // lastPos = new System.Drawing.PointF(x, y);
                                    }
                                }
                            }
                        }
                        else if (path == "s")
                        {
                        }
                    }

                    instructionStr = content.GetNextPath();
                }
                doc.Close();
            }
            return instructions;
        }

        /// <summary>
        /// Rect Instruction
        /// </summary>
        /// <param name="pdfFilePath"></param>
        /// <param name="pageNum"></param>
        /// <returns></returns>
        public static List<RectangleF> GeRectangles(string pdfFilePath, int pageNum)
        {
            List<PdfDrawingInstruction> instructions = PdfDrawingInstructionReader.GetDrawingInstructionsInPage(pdfFilePath, pageNum);
            List<RectangleF> rects = new List<RectangleF>();
            if (instructions != null && instructions.Count > 0)
            {
                foreach (PdfDrawingInstruction pdfDraw in instructions)
                {
                    if (pdfDraw.DrawingType == PdfDrawType.DrawRect)
                    {
                        rects.Add(new RectangleF(
                            pdfDraw.FromPos.X,
                            pdfDraw.FromPos.Y,
                            pdfDraw.ToPos.X - pdfDraw.FromPos.X,
                            pdfDraw.ToPos.Y - pdfDraw.FromPos.Y));
                    }
                }
            }
            return rects;
        }

        /// <summary>
        /// LineTo instruction and Y is the same
        /// </summary>
        /// <param name="pdfFilePath"></param>
        /// <param name="pageNum"></param>
        /// <returns></returns>
        public static List<VerticalLine> GetVLines(string pdfFilePath, int pageNum)
        {
            List<PdfDrawingInstruction> instructions = PdfDrawingInstructionReader.GetDrawingInstructionsInPage(pdfFilePath, pageNum);
            List<VerticalLine> vLines = new List<VerticalLine>();
            if (instructions != null && instructions.Count > 0)
            {
                foreach (PdfDrawingInstruction pdfDraw in instructions)
                {
                    if (pdfDraw.DrawingType == PdfDrawType.DrawLine && pdfDraw.FromPos.X == pdfDraw.ToPos.X)
                    {
                        vLines.Add(new VerticalLine(pdfDraw.FromPos.X, pdfDraw.FromPos.Y, pdfDraw.ToPos.Y));
                    }
                }
            }
            return vLines;
        }

        /// <summary>
        /// LineTo instruction and Y positions are same
        /// </summary>
        /// <param name="pdfFilePath"></param>
        /// <param name="pageNum"></param>
        /// <returns></returns>
        public static List<HorizontalLine> GetHLines(string pdfFilePath, int pageNum)
        {
            List<PdfDrawingInstruction> instructions = PdfDrawingInstructionReader.GetDrawingInstructionsInPage(pdfFilePath, pageNum);
            List<HorizontalLine> hLines = new List<HorizontalLine>();
            if (instructions != null && instructions.Count > 0)
            {
                foreach (PdfDrawingInstruction pdfDraw in instructions)
                {
                    if (pdfDraw.DrawingType == PdfDrawType.DrawLine && pdfDraw.FromPos.Y == pdfDraw.ToPos.Y)
                    {
                        hLines.Add(new HorizontalLine(pdfDraw.FromPos.X, pdfDraw.ToPos.X, pdfDraw.FromPos.Y));
                    }
                }
            }
            return hLines;
        }

    }
}
